<template lang="pug">
view.calendar.bg.white.mod.unt
  view.year-month.flex.mod
    view 预定日期：
    view.flex
      view.mod.before.triangle.left(@click="methods.initMonth(-1)")
      view.bold.large {{data.today.getFullYear()}}年{{data.today.getMonth()+1}}月
      view.mod.before.triangle.right(@click="methods.initMonth(1)")
  view.week.flex
    view.cell(v-for="i in 7") 周{{ '日一二三四五六'[i-1] }}
  view.days.flex.wrap
    view.cell(v-for="n,i in data.days")
      view.empty(v-if="!n")
      view.day.disabled(v-else-if="!data.range.includes(data.calendar_Ymd[i])") {{ n }}
      view.day(v-else :class="{today: data.today_Ymd == data.calendar_Ymd[i], selected: dateSelectedYmd.includes(data.calendar_Ymd[i])}" @click="methods._selected(i)") {{ data.today_Ymd == data.calendar_Ymd[i]? '今': n }}
</template>
<script setup>
import {reactive, computed, watch} from 'vue'
import {useDate} from '/common/script/date'

const emits = defineEmits(['update:dateSelected', 'selected'])
const props = defineProps({
  // 生成可选日期范围相关
  rangeLength: {type: Number, default: 10}, // 可选日期天数
  dateRange: {type: Array, default: [{md: '0812', week: '周六'}]},// 可选日期范围
  // 选中相关
  dateSelected: {type: Array, default: [0]}, // 当前选中的可选范围日期的下标
  multiple: {type: Boolean, default: false}, // 是否多选，所谓选择两个日期间的范围
})

const data = reactive({
  days: 0, // 当前月份天数
  range: [], // 可选范围
  today: new Date(),
  today_Ymd: 0,
  calendar_Ymd: [], // 当前日历月份表数据
})
const methods = {
  /**
   * 重新设置年月
   * @param {number} monthNum 
   */
  initMonth(monthNum = 0){
    data.today.setMonth( data.today.getMonth() + monthNum )
    methods.initCalendar()
  },
  /**
   * 活动某年月的天数
   * 并获取天数对应Ymd值
   */
  initCalendar(){
    const {today} = data
    let [calendar_Ymd, days] = [[], []]
    let month = today.getMonth()
    let year = today.getFullYear()
    // 获取当前月份的第一天
    let firstDayOfMonth = new Date(year, month, 1)
    // 获取当前月份的最后一天
    let lastDayOfMonth = new Date(year, month + 1, 0)
    // 获取当前月份的天数
    let numberOfDays = lastDayOfMonth.getDate()
    // 获取当前月份的第一天是星期几
    let firstDayOfWeek = firstDayOfMonth.getDay()
    // 填充日历数组
    for (let i = 0; i < numberOfDays; i++) {
      days.push(i + 1)
      calendar_Ymd.push(methods.toolYmd(today, i+1))
    }
    // 在日历数组前面添加空白格，以对齐每个日期的位置
    for (let i = 1; i <= firstDayOfWeek; i++) {
      const D = new Date(new Date(`${year}-${month+1}`).getTime() - i * 864e5)
      calendar_Ymd.unshift(methods.toolYmd(D))
      days.unshift('')
    }
    data.days = days
    data.calendar_Ymd = calendar_Ymd
  },
  /**
   * 获取一个日期区域，从今天算
   * 如果有传递过来的区间，则将传递值转成data.range
   */
  initRange(){
    let days = []
    const {dateRange, rangeLength} = props
    if( dateRange.length != rangeLength ){// 是否初始化日期范围，取决于props.dateRange的元素个数
      days = Array(rangeLength).fill(0).map((n,i)=>{
        const today = new Date()
        today.setDate( today.getDate()+i )
        return methods.toolYmd(today)
      })
    }else{
      days = props.dateRange.map(n=>n.Ymd)
    }
    data.range = days
  },
  /**
   * 将一个日期格式为yyyymmdd格式
   * @param {Date} D 
   * @param {number} day 指定日期，-1为使用默认日期
   */
  toolYmd(D, day = -1){
    return [D.getFullYear(), (D.getMonth()+1).toString().padStart(2,'0'), (day==-1? D.getDate(): day).toString().padStart(2,'0')].join('') / 1
  },
  /**
   * 选择一个日期或日期区间
   * @param {number} i 这里的i是月历表的下标，props.dateSelected是可选日期区间的下标，需要换算
   */
  _selected(i){
    const {multiple,dateSelected} = props
    const {range, calendar_Ymd} = data
    const range_i = range.findIndex(n=>n == calendar_Ymd[i])
    let selected = [range_i]
    if(multiple){
      if(dateSelected.length >= 2){
        selected = []
      }else{
        let ary = [...dateSelected, range_i]
        let [min, max] = [Math.min(...ary), Math.max(...ary)]
        if(max > min){
          selected = Array(max - min + 1).fill(0).map((n,i)=>min+i)
        }
      }
    }

    emits('update:dateSelected', selected)
    emits('selected', selected)
  }
}

/**
 * 入口
 */
methods.initCalendar()
data.today_Ymd = methods.toolYmd(new Date())

/**
 * 当props.dateRange有值且长度和取值长度一致时，更新data.range
 */
watch(()=>props.dateRange, (range)=>{
  if(range.length == props.rangeLength) methods.initRange()
})

/**
 * 将选中的可选日期范围的下标，转为月历表的值
 */
const dateSelectedYmd = computed(()=>{
  return props.dateSelected.map(i=>data.range[i])
})

defineExpose({
  range: ()=>{
    // 实际上，这是唯一一次生成日期区间，其他均为调用本次生成的值
    if(data.range.length == 0) methods.initRange()
    return data.range.map(Ymd=>{
      const D = useDate(Ymd.toString().replace(/(\d{4})(\d{2})/, '$1-$2-'))
      return {md: `${D.m}.${D.d}`, week: D.week, Ymd}
    })
  },
})
</script>
<style lang="scss" scoped>
@use '/common/scss/mixin.scss' as *;
.calendar{
  --day-cell-width: 60rpx;
  .cell{
    width: 14.285%;
    text-align: center;
    .day{
      @include hcenter;
      width: var(--day-cell-width);
      height: var(--day-cell-width);
      border-radius: 10rpx;
      margin: 0 auto;
      color: var(--font-color-primary);
      &:not(.empty){
        border: 1px solid var(--day-cell-border, #ddd);
        @include bg(var(--day-cell-bgcolor, #fff));
      }
      &.disabled{
        --day-cell-bgcolor: #F0F0F0;
        --font-color-primary: #9A9A9A;
      }
      &.selected{
        --day-cell-border: var(--bg-color-brown);
        --day-cell-bgcolor: var(--bg-color-brown);
        --font-color-primary: #fff;
      }
    }
  }
  .week{
    padding: 25rpx 0;
    margin-bottom: 25rpx;
    border-top: 1px solid #ddd;
    border-bottom: 1px solid #ddd;
  }
  .days{
    row-gap: 25rpx;
  }
}
</style>